﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Diagnostics;

namespace TechnicalAnalysisEngine.Tests
{
    [TestClass]
    public class TestBollingerBands
    {
        List<double> inputValues;

        List<double> expectedUpperValues;
        List<double> expectedMiddleValues;
        List<double> expectedLowerValues;
        List<double> expectedBandwitdhValues;
        List<double> expectedPercentBValues;

        int expectedBollingerResultCount;
        int expectedBollingerOffset;

        [TestInitialize]
        public void InitializeTest()
        {
            inputValues = new List<double>()
            {
                86.16,89.09,88.78,90.32,89.07,91.15,89.44,89.18,86.93,87.68,86.96,89.43,89.32,88.72,87.45,87.26,89.50,87.90,89.13,90.70,92.90,92.98,
                91.80,92.66,92.68,92.30,92.77,92.54,92.95,93.20,91.07,89.83,89.74,90.40,90.74,88.02,88.09,88.84,90.78,90.54,91.39,90.65

            };

            expectedMiddleValues = new List<double>()
            {
                88.71,89.05,89.24,89.39,89.51,89.69,89.75,89.91,90.08,90.38,90.66,90.86,
                90.88,90.91,90.99,91.15,91.19,91.12,91.17,91.25,91.24,91.17,91.05
            };

            expectedUpperValues = new List<double>()
            {
                91.29,91.95,92.61,92.93,93.31,93.73,93.90,94.27,94.57,94.79,95.04,94.91,
                94.90,94.90,94.86,94.67,94.56,94.68,94.58,94.53,94.53,94.37,94.15

            };

            expectedLowerValues = new List<double>()
            {
                86.12,86.14,85.87,85.85,85.70,85.65,85.59,85.56,85.60,85.98,86.27,86.82,86.87,
                86.91,87.12,87.63,87.83,87.56,87.76,87.97,87.95,87.96,87.95

            };

            expectedBandwitdhValues = new List<double>()
            {
                5.17,5.81,6.75,7.09,7.61,8.08,8.31,8.71,8.97,8.81,8.77,8.09,8.04,7.98,7.74,7.04,6.73,7.12,6.82,6.57,6.58,6.41,6.20
            };

            expectedPercentBValues = new List<double>()
            {
                0.886304167,1.163704742,1.054227856,0.840256627,0.914928436,0.870766802,0.807674365,0.828426895,0.773877343,0.791370003,0.790266268,
                0.525089586,0.369079779,0.35443441,0.42387505,0.441101554,0.028541496,0.073695115,0.159084099,0.428110565,0.393541251,0.534783687,
                0.435419913
            };

            expectedBollingerOffset = 19;
            expectedBollingerResultCount = 23;
        }

        [TestMethod]
        public void TestBollingerBandsMethod()
        {
            var result = AnalysisEngine.BollingerBands(inputValues, 20, 2);

            var upperBand = result.UpperBand;
            var middleBand = result.MiddleBand;
            var lowerBand = result.LowerBand;
            int offset = result.StartIndexOffset;

            Assert.AreEqual(offset, expectedBollingerOffset, "Offset is incorrect, expected" + expectedBollingerOffset + ", result:" + offset);
            Assert.IsTrue(upperBand.Count == expectedBollingerResultCount && 
                middleBand.Count == expectedBollingerResultCount && lowerBand.Count == expectedBollingerResultCount, "Wrong number of objects in resulting lists");

            bool testResult = true;
            string message = null;

            for (int i = 0; i < upperBand.Count; i++)
            {
                if (!TestHelpers.EqualsWithTolerance(upperBand[i], expectedUpperValues[i], 1))
                {
                    message = string.Format("Upper band values do not match at position {0}, expected: {1}, actual: {2} ",
                        i, expectedUpperValues[i], upperBand[i]);

                    testResult = false;
                    break;
                }
                if (!TestHelpers.EqualsWithTolerance(middleBand[i], expectedMiddleValues[i], 1))
                {
                    message = string.Format("Middle band values do not match at position {0}, expected: {1}, actual: {2} ",
                        i, expectedMiddleValues[i], middleBand[i]);

                    testResult = false;
                    break;
                }
                if (!TestHelpers.EqualsWithTolerance(lowerBand[i], expectedLowerValues[i], 1))
                {
                    message = string.Format("Lower band values do not match at position {0}, expected: {1}, actual: {2} ",
                        i, expectedLowerValues[i], lowerBand[i]);

                    testResult = false;
                    break;
                }
            }

            Assert.IsTrue(testResult, message);
        }

        [TestMethod]
        public void TestBollingerBandwidth()
        {
            var result = AnalysisEngine.BollingerBands(inputValues, 20, 2, true, false);

            var upperBand = result.UpperBand;
            var middleBand = result.MiddleBand;
            var lowerBand = result.LowerBand;
            var bandwidth = result.Bandwidth;
            int offset = result.StartIndexOffset;

            Assert.AreEqual(expectedBandwitdhValues.Count, bandwidth.Count);

            bool testResult = true;
            string message = null;

            for (int i = 0; i < bandwidth.Count; i++)
            {
                if (!TestHelpers.EqualsWithTolerance(bandwidth[i], expectedBandwitdhValues[i], 1))
                {
                    message = string.Format("Bandwidth values do not match at position {0}, expected: {1}, actual: {2} ",
                        i, expectedBandwitdhValues[i], bandwidth[i]);

                    testResult = false;
                    break;
                }
            }

            Assert.IsTrue(testResult, message);
        }

        [TestMethod]
        public void TestBollingerPercentB()
        {
            var result = AnalysisEngine.BollingerBands(inputValues, 20, 2, true, true);

            var upperBand = result.UpperBand;
            var middleBand = result.MiddleBand;
            var lowerBand = result.LowerBand;
            var percentB = result.PercentB;
            int offset = result.StartIndexOffset;

            Assert.AreEqual(expectedPercentBValues.Count, percentB.Count);

            bool testResult = true;
            string message = null;

            for (int i = 0; i < percentB.Count; i++)
            {
                if (!TestHelpers.EqualsWithTolerance(percentB[i], expectedPercentBValues[i], 1))
                {
                    message = string.Format("%B values do not match at position {0}, expected: {1}, actual: {2} ",
                        i, expectedPercentBValues[i], percentB[i]);

                    testResult = false;
                    break;
                }
            }

            Assert.IsTrue(testResult, message);
        }

        [TestMethod]
        public void TestBollingerPerformance()
        {
            var upperBand = new List<double>();
            var middleBand = new List<double>();
            var lowerBand = new List<double>();
            var bandwidth = new List<double>();
            var percentB = new List<double>();

            Random random = new Random();
            var randomlyGeneratedValues = new List<double>();

            for (int i = 0; i < 10000; i++)
            {
                randomlyGeneratedValues.Add(random.Next(85, 90));
            }

            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();

            AnalysisEngine.BollingerBands(randomlyGeneratedValues, 20, 20, true, true);

            stopWatch.Stop();
            Console.WriteLine(string.Format("Bollinger, {0} values, {1} μs", randomlyGeneratedValues.Count, stopWatch.ElapsedTicks / 10));
        }
    }
}
